package com.bingxue.edu.common.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.bingxue.edu.common.config.NotifyConfigProperties;
import com.bingxue.edu.common.config.NotifySmsProperties;
import com.bingxue.edu.common.config.SecurityPolicyProperties;
import com.bingxue.edu.common.model.resp.CaptchaResp;
import com.bingxue.edu.framework.common.constant.CacheConstants;
import com.bingxue.edu.framework.common.model.Response;
import com.bingxue.edu.framework.common.util.ValidationUtils;
import com.bingxue.edu.framework.common.validation.MobilePhone;
import com.bingxue.edu.framework.limiter.annotation.RateLimiter;
import com.bingxue.edu.framework.limiter.annotation.RateLimiters;
import com.bingxue.edu.framework.limiter.enums.LimitType;
import com.bingxue.edu.framework.redis.RedisUtils;
import io.springboot.captcha.SpecCaptcha;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.redisson.api.RateIntervalUnit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;

/**
 * 验证码 API
 *
 * @author panbo
 * @since 2024/6/28
 */
@Slf4j
@Tag(name = "验证码接口")
@SaIgnore
@RestController
@RequiredArgsConstructor
@RequestMapping("/captcha")
public class CaptchaController {

    private final SecurityPolicyProperties securityPolicyProperties;
    private final NotifyConfigProperties notifyConfigProperties;
    private final NotifySmsProperties notifySmsProperties;

//    @Log(ignore = true)
    @Operation(summary = "获取图片验证码", description = "获取图片验证码（Base64编码，带图片格式：data:image/gif;base64）")
    @GetMapping("/image")
    public Response<CaptchaResp> getImageCaptcha() {
        int expirationInMinutes = 1;
        String uuid = IdUtil.fastUUID();
        String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + uuid;
        SpecCaptcha captcha = new SpecCaptcha(111, 36, 4);
        long expireTime = LocalDateTimeUtil.toEpochMilli(LocalDateTime.now().plusMinutes(expirationInMinutes));
        RedisUtils.set(captchaKey, captcha.text(), Duration.ofMinutes(expirationInMinutes));
        return Response.success(CaptchaResp.builder().uuid(uuid).img(captcha.toBase64()).expireTime(expireTime).build());
    }


    /**
     * 获取短信验证码
     * 限流规则：
     * 1、同一号码同一模板，1分钟2条，1小时8条，24小时20条
     * 2、同一号码所有模板 24 小时 100 条
     * 3、同一 IP 每分钟限制发送 30 条
     *
     * @param phone      手机号
     */
    @Operation(summary = "获取短信验证码", description = "发送验证码到指定手机号")
    @GetMapping("/sms")
    @RateLimiters({
            @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "MIN", key = "#phone + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.sms.templateId')", rate = 2, interval = 1, unit = RateIntervalUnit.MINUTES, message = "获取验证码操作太频繁，请稍后再试"),
            @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "HOUR", key = "#phone + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.sms.templateId')", rate = 8, interval = 1, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁，请稍后再试"),
            @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "DAY'", key = "#phone + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.sms.templateId')", rate = 20, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁，请稍后再试"),
            @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#phone", rate = 100, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁，请稍后再试"),
            @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#phone", rate = 30, interval = 1, unit = RateIntervalUnit.MINUTES, type = LimitType.IP, message = "获取验证码操作太频繁，请稍后再试")})
    public Response<String> getSmsCaptcha(@NotBlank(message = "手机号不能为空") @MobilePhone String phone) {
        // 生成验证码
        String captcha = RandomUtil.randomNumbers(securityPolicyProperties.getSmsCaptchaLength());
        // 发送验证码
        int expirationInMinutes = securityPolicyProperties.getSmsCaptchaExpireMinutes();
        SmsBlend smsBlend = SmsFactory.getSmsBlend(notifySmsProperties.getConfigId());
        LinkedHashMap<String, String> messageMap = new LinkedHashMap<>(1);
        messageMap.put("1", captcha);
        SmsResponse smsResponse = smsBlend.sendMessage(phone, notifyConfigProperties.getSmsCaptchaTemplateId(), messageMap);
        ValidationUtils.throwIf(smsResponse == null || !smsResponse.isSuccess(), "验证码发送失败, 请稍候再试!");
        // 保存验证码
        String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone;
        RedisUtils.set(captchaKey, captcha, Duration.ofMinutes(expirationInMinutes));
        log.info("短信验证码发送成功, 手机号: {}, 验证码: {}, 有效期: {}分钟", phone, captcha, expirationInMinutes);
        return Response.success("发送成功，验证码有效期 %s 分钟".formatted(expirationInMinutes));
    }
}
